/*********************************************************************************************************
* 模块名称：RTC.c
* 摘    要：RTC模块
* 当前版本：1.0.0
* 作    者：jadewo
* 完成日期：2025年01月14日
* 内    容：RTC实时时钟功能
* 注    意：
**********************************************************************************************************
* 取代版本：
* 作    者：
* 完成日期：
* 修改内容：
* 修改文件：
*********************************************************************************************************/

/*********************************************************************************************************
*                                              包含头文件
*********************************************************************************************************/
#include "RTC.h"
#include "gd32f30x_conf.h"
#include <stdio.h>

/*********************************************************************************************************
*                                              宏定义
*********************************************************************************************************/

/*********************************************************************************************************
*                                              枚举结构体
*********************************************************************************************************/

/*********************************************************************************************************
*                                              内部变量定义
*********************************************************************************************************/

static uint8_t const month_table[12]={31,28,31,30,31,30,31,31,30,31,30,31}; //平年月份的天数
 
static uint32_t timecount = 0;

static _calender_obj calender;

static  unsigned char  s_iRtc1sFlag  = FALSE;    //将RTC 1s标志位的值设置为FALSE
/*********************************************************************************************************
*                                              内部函数声明
*********************************************************************************************************/
static void ConfigRTC(void);    //配置RTC实时时钟

static uint8_t RTC_Get(void);
static uint8_t Is_Leap_Year(uint16_t year);
/*********************************************************************************************************
*                                              内部函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称：ConfigRTC
* 函数功能：配置RTC时钟
* 输入参数：void
* 输出参数：void
* 返 回 值：void
* 创建日期：2025年01月14日
* 注    意：RTC使用外部低速晶振，频率32768，不使用日历功能，仅使用秒中断
*********************************************************************************************************/
static void ConfigRTC(void)
{
    rcu_periph_clock_enable(RCU_BKPI); //备份区域的时钟要先使能
	rcu_periph_clock_enable(RCU_PMU);  //电源管理时钟使能
	pmu_backup_write_enable();         //使能备份域访问允许

    
    /* 一般选择外部低速时钟，更为稳定，此次实验所用单片机不包含外部低速时钟 */
	rcu_osci_on(RCU_LXTAL);            //使能内部时钟
	rcu_osci_stab_wait(RCU_LXTAL);     //等待时钟稳定
	rcu_rtc_clock_config(RCU_RTCSRC_LXTAL); //时钟源选择
	
	rcu_periph_clock_enable(RCU_RTC);   //使能RTC时钟
	rtc_register_sync_wait();           //等待寄存器与APB1时钟同步
	rtc_lwoff_wait();                   //等待RTC的最后一次操作完成
	rtc_interrupt_enable(RTC_INT_SECOND);//使能RTC的秒中断
	rtc_lwoff_wait();                   //等待RTC的最后一次操作完成
 
	rtc_prescaler_set(32767);           // 配置RTC_PRL的值(时钟分频)，一般选择32767 
	rtc_lwoff_wait();                   //等待RTC的最后一次操作完成
 
	nvic_irq_enable(RTC_IRQn, 2, 0);    //配置中断的优先级

    uint16_t bkp0 = bkp_read_data(BKP_DATA_0);
    if(bkp0 != 0x5A5A){
        bkp_deinit();                           //备份域复位
        bkp_write_data(BKP_DATA_0, 0x5A5A);
        RTC_Set(2025, 1, 14, 10, 28, 15);
    }
}


/*********************************************************************************************************
* 函数名称：RTC_Get
* 函数功能：转换RTC时间
* 输入参数：void
* 输出参数：void
* 返 回 值：void
* 创建日期：2025年01月14日
* 注    意：RTC使用外部低速晶振，频率32768，不使用日历功能，仅使用秒中断
*********************************************************************************************************/
static uint8_t RTC_Get(void){
    uint32_t temp_seconds;
	//把timecount转换为日期时间，并赋给calender
	uint32_t temp_days ;
	uint16_t temp_year = 1970;
	uint16_t temp_month;
	
	timecount = rtc_counter_get();//读取RTC_CNT寄存器的值
    temp_days = timecount/86400;
 
	//处理天数中的整年，
	if(temp_days>0){
		while(temp_days>=365){
			if(Is_Leap_Year(temp_year)){//如果是闰年
				if(temp_days>365){
					temp_days -= 366;
				}
				else{
					break;
				}
			}else{
				temp_days -= 365;
			}
			temp_year++;
		}
		calender.w_year = temp_year;
		
		//剩下不足一年的，再处理整月
		temp_month = 1; //用来临时存放月份
		while(temp_days >= 28){ //超过了一个月
			if(Is_Leap_Year(calender.w_year) && temp_month == 2){
				if(temp_days>=29){ //闰年的2月是29天
					temp_days -= 29;
				}else{
					break;
				}
			}else{
				if(temp_days >= month_table[temp_month-1]){//剩余的天数是不是大于temp_month这个月整月的天数
					temp_days -= month_table[temp_month-1];
				}else{
					break;
				}
			}
			temp_month++;
		}
	}
 
	calender.w_month = temp_month;
	calender.w_day = temp_days+1;
	
	//处理剩下的不足一天的秒数，时：分：秒
	temp_seconds = timecount % 86400; //不足一天的秒数
	calender.hour = temp_seconds / 3600;
	calender.min = (temp_seconds % 3600) / 60;
	calender.sec = temp_seconds % 60;
	
	return 0;
}
 
/*********************************************************************************************************
* 函数名称：Is_Leap_Year
* 函数功能：判断year是否闰年
* 输入参数：void
* 输出参数：void
* 返 回 值：是润年返回1，否则返回0
* 创建日期：2025年01月14日
* 注    意：
*********************************************************************************************************/
static uint8_t Is_Leap_Year(uint16_t year){ 
	if(year%4 == 0){
		if(year%100 == 0){
			if(year%400 == 0)
				return 1;
			else
				return 0;
		}else{
			return 1;
		}
	}else{
		return 0;
	}
}

/*********************************************************************************************************
* 函数名称：RTC_IRQHandler
* 函数功能：RTC中断服务函数
* 输入参数：void
* 输出参数：void
* 返 回 值：void
* 创建日期：2025年01月14日
* 注    意：使用秒中断，处理每秒任务
*********************************************************************************************************/
void RTC_IRQHandler(void)
{
	if(rtc_flag_get(RTC_FLAG_SECOND) != RESET){ //判断是否为秒中断
		rtc_flag_clear(RTC_FLAG_SECOND);
		s_iRtc1sFlag = TRUE;    //秒中断标志位置1
        RTC_Get();              //转换RTC时间
    }
}
    
/*********************************************************************************************************
*                                              API函数实现
*********************************************************************************************************/
/*********************************************************************************************************
* 函数名称：InitRTC
* 函数功能：初始化RTC实时时钟
* 输入参数：void
* 输出参数：void
* 返 回 值：void
* 创建日期：2025年01月14日
* 注    意：
*********************************************************************************************************/
void InitRTC(void)
{
    ConfigRTC();
}

/*********************************************************************************************************
* 函数名称：GetRTC1sFlag
* 函数功能：返回RTC 1s标志位
* 输入参数：void
* 输出参数：void
* 返 回 值：RTC 1s 标志位的值
* 创建日期：2025年01月14日
* 注    意：
*********************************************************************************************************/
unsigned char GetRTC1sFlag(void)
{
    return s_iRtc1sFlag;
}


/*********************************************************************************************************
* 函数名称：ClrRTC1sFlag
* 函数功能：清除RTC 1s标志位
* 输入参数：void
* 输出参数：void
* 返 回 值：void
* 创建日期：2025年01月14日
* 注    意：
*********************************************************************************************************/
void ClrRTC1sFlag(void)
{
    s_iRtc1sFlag = FALSE;
}


/*********************************************************************************************************
* 函数名称：RTC_Set
* 函数功能：设置RTC时钟
* 输入参数：void
* 输出参数：void
* 返 回 值：设置是否出错，1出错，0正常
* 创建日期：2025年01月14日
* 注    意：
*********************************************************************************************************/
uint8_t RTC_Set(uint16_t syear, uint8_t smonth, uint8_t sday, uint8_t shour, uint8_t smin, uint8_t ssec){
	uint32_t seccounts = 0;
	uint16_t temp_year = 1970;
	uint8_t temp_month;
	if(syear<1970 || syear>2099){  //设置的时间不合理
		return 1;
	}
	
	//整年的秒数
	while(temp_year < syear){
		if(Is_Leap_Year(temp_year))seccounts += 31622400; //闰年，一年的秒数  366* 24 * 60 *60
		else seccounts += 31536000;  //平年，一年的秒数  365* 24 * 60 *60
		temp_year++;
	}
 
	//整月的秒数
	smonth--;
	for(temp_month = 0; temp_month<smonth; temp_month++){
		seccounts += (uint32_t)month_table[temp_month]*86400;
		if(Is_Leap_Year(syear)&&temp_month==1)seccounts += 86400; //如果设置的年份是闰年，在二月这个月份要加多一天
	}
	
	//日、时、分、秒的处理
	seccounts += (uint32_t)(sday-1)*86400; //整日的秒数  24 * 60 * 60
	seccounts += (uint32_t)shour*3600;//小时
	seccounts += (uint32_t)smin*60;   //分
	seccounts += ssec;      //秒
	
	rtc_lwoff_wait();
	rtc_counter_set(seccounts);
	return 0;
}

/*********************************************************************************************************
* 函数名称：getRTC
* 函数功能：获取当前日历信息
* 输入参数：void
* 输出参数：void
* 返 回 值：当前日历
* 创建日期：2025年01月14日
* 注    意：
*********************************************************************************************************/
_calender_obj getRTC(void)
{
    return calender;
}
